import inspect, itertools, copy

def isdescriptor(obj):
    return hasattr(obj, '__get__') or hasattr(obj, '__set__')


class Attribute(object):
    
    def __init__(self, **kw):
        for name, data in self.static():
            setattr(self, name, kw.get(name, data.default))
        
    def static(self):
        for name, value in inspect.getmembers(self.__class__):
            if isinstance(value, StaticData):
                yield (name, value)
    
    def __set__(self, instance, value):
        return instance._set_data(self, value)
    
    def __get__(self, instance, owner=None):
        if owner is not None:
            return self
        return instance._get_data(self)

class StaticData(object):
    
    def __init__(self, default):
        self.default = default


class Int(Attribute):
    default = 0
    min = StaticData(None)
    max = StaticData(None)

    def __set__(self, instance, value):
        if self.min is not None and value < self.min:
            raise ValueError("Value is less then minimum")
        if self.max is not None and value > self.max:
            raise ValueError("Value is more then maximum")
        return instance._set_data(self, value)
        
    def __get__(self, instance, owner=None):
        return instance._get_data(self)


class ComponentMeta(type):
    
    def __new__(self, name, bases, ns):
        cls = type.__new__(self, name, bases, ns)
        if not hasattr(cls, '_attributes'):
            cls._attributes = []
        cls._attributes = []
        items = ns.items()
        cls.order = []
        if ns.has_key("__order__"):
            cls.order = ns.get('__order__')
        if not globals().has_key('ComponentBase'):
            return cls
        for name, value in ns.items():
            if isinstance(value, (Attribute, ComponentBase,)):
                value.name = name
                cls._attributes.append(value)
        cls._attributes.sort(cmp=cls.attr_sort )
        return cls
    
    def attr_sort(self, attr1, attr2):
        if not attr1 in self.order and attr2 in self.order:
            return 1
        if attr1 in self.order and not attr2 in self.order:
            return -1
        if not attr1 in self.order and not attr2 in self.order:
            return 0
        
        id1 = self.order.index(attr1)
        id2 = self.order.index(attr2)
        return cmp(id1, id2)
            

class DataIter(object):
    def __init__(self, data, start=0):
        self.data = data
        self.index = start
        
    def __iter__(self): return self
        
    def next(self):
        attr = self.data.attr_at(self.index)
        if attr is None:
            raise StopIterations()
        self.index += 1
        return getattr(self.data, attr.name)

class ComponentBase(object):
    __metaclass__ = ComponentMeta
    
    default = None
    
    def __init__(self, **kw):
        self.name = None
        self.data = {}
        self.default = kw.get('default', self.__class__.default)
        
        
        for name, data in self.static():
            setattr(self, name, kw.get(name, data.default))
        
        
        for attr in self.attrs():
            if isinstance(attr, Attribute):
                setattr(self, attr.name, kw.get(attr.name, attr.default))
            elif isinstance(attr, ComponentBase):
                c = attr.copy()
                self._set_data(self, c)
                #self.set_copy(attr)
                if kw.get(attr.name):
                    setattr(self, attr.name, kw.get(attr.name, attr.default))
    
    def static(self):
        for name, value in self.__class__.__dict__.items():
            if isinstance(value, StaticData):
                yield (name, value)
    
    def __len__(self): return len(list(self.attrs()))
    
    def copy(self):
        c = self.__class__()
        
        c.name = self.name
        
        for name, value in self.data.items():
            setattr(c, name, value)
        
        for name, value in self.static():
            setattr(c, name, getattr(self, name))
        
        return c
    
    def attrs(self):
        for attr in self._attributes:
            yield attr
    
    def num_attrs(self): return len(list(self.attrs()))
    
    def static_attr(self, name):
        for attr in self._attributes:
            if attr.static and attr.name == name:
                return attr
    
    def add_attr(self, attr, value):
        self._attributes.append(attr)
        setattr(self.__class__, attr.name, attr)
        setattr(self, attr.name, value)
        
    def del_attr(self, attr):
        del self._attributes[self._attributes.index(attr)]
        # There must be a better way then this
        exec("del "+self.__class__.__name__+"."+attr.name)
    
    def attr_at(self, index):
        return itertools.islice(self.attrs(), index, index+1).next()
    
    def attr_named(self, name):
        return itertools.ifilter(lambda attr: attr.name == name, self.attrs())
    
    def __iter__(self): return DataIter(self)
    
    def __set__(self, instance, value):
        val_type_name = value.__class__.__name__
        convert_fn = 'from_'+val_type_name
        if hasattr(self, convert_fn):
            getattr(self, convert_fn)(value)
            instance._set_data(self, self)
            return
        
        raise ValueError("Dont know how to convert "+ str(value.__class__.__name__) + " to " + self.__class__.__name__)
    
    def __get__(self, instance, owner=None):
        return instance._get_data(self)
    
    def __getitem__(self, key):
        if isinstance(key, int):
            attr = self.attr_at(key)
        elif isinstance(key, str):
            attr = self.attr_named(key)
        else:
            raise AttributeError("Key must be int or string")
        return self.get_data(attr)
    
    def __setitem__(self, key, value):
        if isinstance(key, int):
            attr = self.attr_at(key)
        elif isinstance(key, str):
            attr = self.attr_named(key)
        else:
            raise AttributeError("Key must be int or string")
        self.set_data(attr, value)

    def get_data(self, attr):
        return getattr(self, attr.name)
    def set_data(self, attr, value):
        setattr(self, attr.name, value)

    def _get_data(self, attr):
        return self.data[attr.name]
    def _set_data(self, attr, value):
        self.data[attr.name] = value
        
    @classmethod
    def _get_static_data(cls, attr):
        getattr(cls, attr.name)


class Component(ComponentBase):
    pass

    
class Color(Component):
    x = Int(min=0, max=255, default=0)
    y = Int(min=0, max=255, default=0)
    z = Int(min=0, max=255, default=0)
    
    __order__ = [x,y,z]
    
    def from_Color(self, color):
        self.x, self.y, self.z = color.x, color.y, color.z
        
    def from_int(self, i):
        self.x, self.y, self.z = i,i,i


class ColorTypes(Component):
    red = Color(x=255)
    green = Color(y=255)
    blue = Color(z=255)



class DataType(Attribute):
    default = Attribute
    
    def __get__(self, instance, owner=None):
        return instance._get_data(self)
    
    def __set__(self, instance, value):
        if not issubclass(value, Attribute):
            raise TypeError("Must be of type Data")
        instance._set_data(self, value)
    
    


class List(Component):
    
    itemtype = StaticData(Attribute())
        
    def add(self, value):
        attr = copy.deepcopy(self.itemtype) #.copy()
        attr.name = self.index_name(len(self))
        self.add_attr(attr, value)
        
    def rem(self, index):
        self.del_attr(self.attr_at(index))
        
    def index_name(self, index):
        return "id_" + str(index)
        
    def clear(self):
        for attr in list(self.attrs()):
            self.del_attr(attr)
        
    def set_index(self, value, index):
        length = len(self)
        if length > index:
            setattr(self, self.index_name(index), value)
        elif length == index:
            self.add(value)
        else:
            raise ValueError()
        
    def from_list(self, L):
        print 'from list', self, L
        self.clear()
        for item in L:
            self.add(item)
            
    def from_List(self, L):
        self.clear()
        for item in L:
            self.add(item)
            
    def from_NoneType(self, n):
        self.clear()

        
class Palette(Component):
    colors = List(itemtype=Color(x=10))
    outColor = Color()

class DataProperty(object):
    def __init__(self, parent=None, attr_name=None):
        self.parent = parent
        self.attr_name = attr_name
        
    def __get__(self, instance, owner=None):
        #if self.parent is None or self.attr_name is None:
        #    return
        if owner is None: return self
        return getattr(instance.parent, instance.attr_name)
    
    def __set__(self, instance, value):
        #if self.parent is None or self.attr_name is None:
        #    return
        return setattr(instance.parent, instance.attr_name, value)

color = Color(y=100, z=120)
assert color.x == 0
assert color.y == 100
assert color.z == 120

assert color[0] == color.x
assert color[1] == color.y
assert color[2] == color.z

colort = ColorTypes()
colort2 = ColorTypes()


assert colort.red.x == 255
assert colort.green.y == 255
assert colort.blue.z == 255

assert type(colort.red) is Color

assert colort.red is not colort2.red

p = Palette(colors = [Color(x=30)])
p2 = Palette()
p2.colors = []
assert type(p.colors.itemtype) is Color
assert len(p.colors) == 1
assert p.colors[0].x == 30

p.colors.add(Color(y=10, z=30))
assert len(p.colors) == 2
assert p.colors[1].y == 10


p.colors.rem(0)
assert len(p.colors) == 1


p.colors = [Color(x=100), Color(y=100)]
assert isinstance(p.colors, List)
assert p.colors[0].x == 100
assert p.colors[1].y == 100
assert p.colors.id_0.x == 100


class View(object):
    data = DataProperty()
    
    def data_change(self):
        pass
        
    def set_attr(self, obj, attr):
        self.parent = obj
        self.attr_name = attr
        
    def widget_change(self):
        self.data = 10
        
        
view = View()
view.set_attr(p.colors, "id_1")
view.widget_change()
